package com.xuln.common.base.dao;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.xuln.common.base.exception.DaoException;
import com.xuln.common.util.GenericsUtils;

/**
 * @author 徐炉南 xinlunan487@163.com
 *
 * @param <T>
 */
/**
 * @author 徐炉南 xinlunan487@163.com
 * @param <T>
 */
@Service
@Transactional
public class BaseDao<T> {
	protected Class<T> entityClass = GenericsUtils.getSuperClassGenricType(this.getClass());

	@PersistenceContext
	protected EntityManager entityManager;

	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * 分离所有当前正在被管理的实体<br>
	 * 在处理大量实体的时候，如果不把已经处理过的实体从EntityManager中分离出来，将会消耗大量的内存。 调用EntityManager
	 * 的clear()方法后，所有正在被管理的实体将会从持久化内容中分离出来。
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22 void
	 */
	public void clear() {
		entityManager.clear();
	}

	public void close() {
		entityManager.close();
	}

	/**
	 * 检查entity是否在EntityManager管理当中<br>
	 * 如果这个实体对象当前正被持久化内容管理，返回值为true，否则为false
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param arg0
	 * @return boolean
	 */
	public boolean contains(Object entity) {
		return entityManager.contains(entity);
	}

	public Query createNativeQuery(String arg0, Class arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	public Query createNativeQuery(String arg0, String arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	public Query createNativeQuery(String sql) {
		// TODO Auto-generated method stub
		return null;
	}

	public Query createQuery(String hql) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 删除实体 <br>
	 * 参数形式：entity[] 或entity1,entity2,entity3... <br>
	 * 如果级联关系cascade=CascadeType.ALL，在删除person 时候，也会把级联对象删除。
	 * 把cascade属性设为cascade=CascadeType.REMOVE 有同样的效果。
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-21
	 * @param entity void
	 */
	public void delete(Object... entitys) {
		for (Object entity : entitys) {
			// 注意：要删除一个对象的时候不能够直接调用entityManager.remove()进行，因为原来的session已经关闭，无法执行remove方法。<br>
			// 必须首先进行更新，这样新的session产生才可以删除。可删除merge()方法更新的对象<br>
			entityManager.remove(entityManager.merge(entity));
		}
	}

	/**
	 * 通过ids删除实体 <br>
	 * 参数形式："1,2,3..." 或 1,2,3... 或对象形式ID<br>
	 * 如果级联关系cascade=CascadeType.ALL，在删除person 时候，也会把级联对象删除。
	 * 把cascade属性设为cascade=CascadeType.REMOVE 有同样的效果。
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-21
	 * @param ids void
	 */
	public void deleteByIds(Serializable... ids) {
		for (Object id : ids) {
			// 参数形式:"1,2,3,4"
			if ((id instanceof String) && ((String) id).contains(",")) {
				String tempIds[] = ((String) id).split(",");
				for (Object tempId : tempIds) {
					// 注意：要删除一个对象的时候不能够直接调用entityManager.remove()进行，因为原来的session已经关闭，无法执行remove方法。<br>
					// 必须首先进行更新，这样新的session产生才可以删除。可删除getReference()方法得到的对象
					entityManager.remove(entityManager.getReference(this.entityClass, Long.valueOf((String) tempId)));
				}
			}
			// 参数形式:1
			else if (id instanceof Integer) {
				entityManager.remove(entityManager.getReference(this.entityClass, Long.valueOf((Integer) id)));
			} else {
				entityManager.remove(entityManager.getReference(this.entityClass, id));
			}
		}
	}

	/**
	 * 通过ID得到实体<br>
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param id
	 * @return T
	 */
	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public T find(Serializable id) {
		if (id instanceof Integer) {
			// 当在数据库中没有找到记录时，find()方法会返回null，而getReference()方法会抛出javax.persistence.EntityNotFoundException例外.
			// 另外getReference()方法不保证entity Bean已被初始化。
			// 如果传递进getReference()或find()方法的参数不是实体Bean，都会引发IllegalArgumentException例外
			return entityManager.find(this.entityClass, Long.valueOf((Integer) id));
		} else {
			return entityManager.find(this.entityClass, id);
		}
	}

	public <T> T find(Class<T> arg0, Object arg1) {
		// TODO Auto-generated method stub
		return null;
	}

	/**
	 * 将实体的改变立刻刷新到数据库中<br>
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22 void
	 */
	public void flush() {
		entityManager.flush();
	}

	public EntityTransaction getTransaction() {
		return entityManager.getTransaction();
	}

	public boolean isOpen() {
		return entityManager.isOpen();
	}

	public void joinTransaction() {
		entityManager.joinTransaction();
	}

	public T loadAll() {
		// TODO Auto-generated method stub
		return null;
	}

	public void lock(Object entity, LockModeType lockModeType) {
		entityManager.lock(entity, lockModeType);
	}

	public void persist(Object entity) {
		entityManager.persist(entity);
	}

	/**
	 * 刷新实体,把数据库中的新值重写进实体
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param entity void
	 * @throws Exception
	 */
	public void refresh(Object entity) throws DaoException {
		try {
			entityManager.refresh(entity);
		} catch (RuntimeException e) {
			throw new DaoException(this.entityClass.getName() + ": 刷新实体时出错", e);
		}
	}

	/**
	 * 保存实体到数据库<br>
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param entity void
	 */
	public void save(Object entity) {
		this.persist(entity);
	}

	/**
	 * 保存或更新实体到数据库<br>
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param entity
	 * @return T
	 */
	public T saveOrUpdate(T entity) {
		return entityManager.merge(entity);
	}

	/**
	 * 保存或更新实体到数据库<br>
	 * 效果与saveOrUpdate相同
	 * 
	 * @lastmodify : 徐炉南 xinlunan487@163.com 2010-4-22
	 * @param entity
	 * @return T
	 */
	public T update(T entity) {
		return this.saveOrUpdate(entity);
	}

	@Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED)
	public long getCount() {
		return (Long) entityManager.createQuery(
				"select count(" + getCountField(this.entityClass) + ") from " + getEntityName(this.entityClass) + " o")
				.getSingleResult();
	}

	protected static void setQueryParams(Query query, Object[] queryParams) {
		if (queryParams != null && queryParams.length > 0) {
			for (int i = 0; i < queryParams.length; i++) {
				query.setParameter(i + 1, queryParams[i]);
			}
		}
	}

	/**
	 * 组装order by语句
	 * 
	 * @param orderby
	 * @return
	 */
	protected static String buildOrderby(LinkedHashMap<String, String> orderby) {
		StringBuffer orderbyql = new StringBuffer("");
		if (orderby != null && orderby.size() > 0) {
			orderbyql.append(" order by ");
			for (String key : orderby.keySet()) {
				orderbyql.append("o.").append(key).append(" ").append(orderby.get(key)).append(",");
			}
			orderbyql.deleteCharAt(orderbyql.length() - 1);
		}
		return orderbyql.toString();
	}

	/**
	 * 获取实体的名称
	 * 
	 * @param <E>
	 * @param clazz 实体类
	 * @return
	 */
	protected static <E> String getEntityName(Class<E> clazz) {
		String entityname = clazz.getSimpleName();
		Entity entity = clazz.getAnnotation(Entity.class);
		if (entity.name() != null && !"".equals(entity.name())) {
			entityname = entity.name();
		}
		return entityname;
	}

	protected static <E> String getCountField(Class<E> clazz) {
		String out = "o";
		try {
			PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
			for (PropertyDescriptor propertydesc : propertyDescriptors) {
				Method method = propertydesc.getReadMethod();
				if (method != null && method.isAnnotationPresent(EmbeddedId.class)) {
					PropertyDescriptor[] ps = Introspector.getBeanInfo(propertydesc.getPropertyType())
							.getPropertyDescriptors();
					out = "o." + propertydesc.getName() + "."
							+ (!ps[1].getName().equals("class") ? ps[1].getName() : ps[0].getName());
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return out;
	}

}
